package org.apache.commons.graph.model

import org.apache.commons.graph.CommonsGraph.synchronize
import org.apache.commons.graph.GraphException
import org.apache.commons.graph.MutableGraph
import org.apache.commons.graph.utils.GraphUtils
import org.apache.commons.graph.utils.MultiThreadedTestRunner
import org.apache.commons.graph.utils.TestRunner
import org.junit.Assert
import org.junit.Test
import kotlin.test.assertEquals

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/

/**
 *
 */
class BaseMutableGraphTestCase {
    // Utility class.
    private inner class GraphInsert
        (
        private val g: MutableGraph<BaseLabeledVertex, BaseLabeledEdge>,
        private val start: Int,
        private val end: Int
    ) : TestRunner() {
        override fun runTest() {
            // building a complete Graph

            for (i in start..<end) {
                val v = BaseLabeledVertex(i.toString())
                g.addVertex(v)
            }
            synchronized(g) {
                for (v1 in g.vertices) {
                    for (v2 in g.vertices) {
                        if (v1 != v2) {
                            try {
                                val e = BaseLabeledEdge("$v1 -> $v2")
                                g.addEdge(v1, e, v2)
                            } catch (e: GraphException) {
                                // ignore
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * Test method for
     * [org.apache.commons.graph.model.BaseMutableGraph.addVertex].
     */
    @Test
    fun testAddVertexAndEdge() {
        // Test a complete undirect graph.

        val g =
            UndirectedMutableGraph<BaseLabeledVertex, BaseLabeledEdge>()
        GraphUtils.buildCompleteGraph(50, g)

        Assert.assertEquals(50, g.order.toLong())
        Assert.assertEquals(1225, g.size.toLong())
        for (v in g.vertices) {
            Assert.assertEquals(49, g.getDegree(v).toLong())
        }

        // Test a complete direct graph.
        val gDirect =
            DirectedMutableGraph<BaseLabeledVertex, BaseLabeledEdge>()
        GraphUtils.buildCompleteGraph(50, gDirect)

        Assert.assertEquals(50, gDirect.order.toLong())
        Assert.assertEquals(2450, gDirect.size.toLong())
        for (v in gDirect.vertices) {
            Assert.assertEquals(98, gDirect.getDegree(v).toLong())
        }

        // Test
        val gSimple =
            DirectedMutableGraph<BaseLabeledVertex, BaseLabeledEdge>()
        val one = BaseLabeledVertex("1")
        val two = BaseLabeledVertex("2")
        gSimple.addVertex(one)
        gSimple.addVertex(two)

        gSimple.addEdge(one, BaseLabeledEdge("1 -> 2"), two)

        Assert.assertEquals(2, gSimple.order.toLong())
        Assert.assertEquals(1, gSimple.size.toLong())
        Assert.assertEquals(1, gSimple.getDegree(one).toLong())
        Assert.assertEquals(1, gSimple.getDegree(two).toLong())
        Assert.assertEquals(0, gSimple.getInDegree(one).toLong())
        Assert.assertEquals(1, gSimple.getInDegree(two).toLong())
        Assert.assertEquals(1, gSimple.getOutDegree(one).toLong())
        Assert.assertEquals(0, gSimple.getOutDegree(two).toLong())
        Assert.assertFalse(gSimple.containsEdge(BaseLabeledEdge("Not Exist Edge")))
        Assert.assertFalse(gSimple.containsVertex(BaseLabeledVertex("Not exist vertex")))
    }

    /**
     * Test method for
     * [org.apache.commons.graph.model.BaseMutableGraph.removeEdge]
     */
    @Test
    fun testDirectedGraphRemoveEdge() {
        // Test a complete undirect graph.
        val g = DirectedMutableGraph<BaseLabeledVertex, BaseLabeledEdge>()

        val source = BaseLabeledVertex(1.toString())
        val target = BaseLabeledVertex(2.toString())

        GraphUtils.buildCompleteGraph(10, g)

        val e = g.getEdge(source, target)!!
        g.removeEdge(e)

        val edge = g.getEdge(source, target)
        Assert.assertNull(edge)
    }

    /**
     * Test method for [org.apache.commons.graph.model.BaseMutableGraph.removeEdge]
     */
    @Test(expected = GraphException::class)
    fun testDirectedGraphRemoveEdgeNotExists() {
        // Test a complete undirect graph.
        var g: DirectedMutableGraph<BaseLabeledVertex, BaseLabeledEdge>? = null
        var e: BaseLabeledEdge? = null
        try {
            g = DirectedMutableGraph()
            GraphUtils.buildCompleteGraph(10, g!!)

            e = BaseLabeledEdge("NOT EXIST")
        } catch (ex: GraphException) {
            Assert.fail(ex.message)
        }

        g!!.removeEdge(e!!)
    }

    /**
     * Test Graph model in a multi-thread enviroment.
     */
    @Test
    @Throws(Throwable::class)
    fun testDirectedMultiTh() {
        val g =
            synchronize(DirectedMutableGraph<BaseLabeledVertex, BaseLabeledEdge>() as MutableGraph<BaseLabeledVertex, BaseLabeledEdge>) as MutableGraph<BaseLabeledVertex, BaseLabeledEdge>
        val tr1: TestRunner = GraphInsert(g, 0, 10)
        val tr2: TestRunner = GraphInsert(g, 10, 20)
        val tr3: TestRunner = GraphInsert(g, 20, 30)

        val trs = arrayOf(tr1, tr2, tr3)
        val mttr = MultiThreadedTestRunner(trs)
        mttr.runRunnables()

        assertEquals(30, g.order)

        // test the # of edges = n (n-1)
        assertEquals((30 * (30 - 1)), g.size)
    }

    /**
     * Test method for
     * [org.apache.commons.graph.model.BaseGraph.getConnectedVertices]
     */
    @Test
    fun testGetConnectedVertices() {
        // Test a complete undirect graph.
        val g =
            UndirectedMutableGraph<BaseLabeledVertex, BaseLabeledEdge>()
        GraphUtils.buildCompleteGraph(10, g)

        val testVertex = BaseLabeledVertex(1.toString())
        val connectedVertices = g.getConnectedVertices(testVertex)
        Assert.assertNotNull(connectedVertices)

        val v: MutableList<BaseLabeledVertex> = ArrayList()
        for (baseLabeledVertex in connectedVertices) {
            v.add(baseLabeledVertex)
        }

        Assert.assertEquals(9, v.size.toLong())
        Assert.assertFalse(v.contains(testVertex))
    }

    /**
     * Test method for
     * [org.apache.commons.graph.model.BaseGraph.getConnectedVertices]
     */
    @Test(expected = GraphException::class)
    fun testGetConnectedVerticesNPE() {
        // Test a complete undirect graph.
        var g: UndirectedMutableGraph<BaseLabeledVertex, BaseLabeledEdge>? = null
        var notExistsVertex: BaseLabeledVertex? = null
        try {
            g = UndirectedMutableGraph()
            GraphUtils.buildCompleteGraph(10, g)

            notExistsVertex = BaseLabeledVertex(1000.toString())
        } catch (e: GraphException) {
            Assert.fail(e.message)
        }
        g!!.getConnectedVertices(notExistsVertex!!)
    }

    /**
     * Test method for
     * [org.apache.commons.graph.model.BaseGraph.getConnectedVertices]
     */
    @Test
    fun testGetConnectedVerticesOnNotConnectedGraph() {
        // Test a complete undirect graph.
        val g =
            UndirectedMutableGraph<BaseLabeledVertex, BaseLabeledEdge>()

        // building a not connected Graph
        for (i in 0..3) {
            val v = BaseLabeledVertex(i.toString())
            g.addVertex(v)
        }

        val testVertex = BaseLabeledVertex(1.toString())
        val connectedVertices = g.getConnectedVertices(testVertex)
        Assert.assertNotNull(connectedVertices)

        val v: MutableList<BaseLabeledVertex> = ArrayList()
        for (baseLabeledVertex in connectedVertices) {
            v.add(baseLabeledVertex)
        }

        Assert.assertEquals(0, v.size.toLong())
    }

    /**
     * Test method for
     * [org.apache.commons.graph.model.BaseGraph.getEdge]
     */
    @Test
    fun testGetEdge() {
        // Test a complete undirect graph.
        val g =
            UndirectedMutableGraph<BaseLabeledVertex, BaseLabeledEdge>()
        GraphUtils.buildCompleteGraph(10, g)

        val source = BaseLabeledVertex(1.toString())
        val target = BaseLabeledVertex(2.toString())
        val edge = g.getEdge(source, target)
        Assert.assertNotNull(edge)
    }

    /**
     * Test method for
     * [org.apache.commons.graph.model.BaseGraph.getEdge]
     */
    @Test(expected = GraphException::class)
    fun testGetEgdeNotExistsVertex() {
        // Test a complete undirect graph.
        var g: UndirectedMutableGraph<BaseLabeledVertex, BaseLabeledEdge>? = null
        var source: BaseLabeledVertex? = null
        var target: BaseLabeledVertex? = null
        try {
            g = UndirectedMutableGraph()
            GraphUtils.buildCompleteGraph(10, g)

            source = BaseLabeledVertex(100.toString())
            target = BaseLabeledVertex(2.toString())
        } catch (e: GraphException) {
            Assert.fail(e.message)
        }

        g!!.getEdge(source!!, target!!)
    }

    /**
     * Test method for
     * [org.apache.commons.graph.model.BaseGraph.getEdge]
     */
    @Test(expected = GraphException::class)
    fun testGetEgdeNotExistsVertex_2() {
        // Test a complete undirect graph.

        var g: UndirectedMutableGraph<BaseLabeledVertex, BaseLabeledEdge>? = null
        var source: BaseLabeledVertex? = null
        var target: BaseLabeledVertex? = null
        try {
            g = UndirectedMutableGraph()
            GraphUtils.buildCompleteGraph(10, g)

            source = BaseLabeledVertex(1.toString())
            target = BaseLabeledVertex(200.toString())
        } catch (e: GraphException) {
            Assert.fail(e.message)
        }

        g!!.getEdge(source!!, target!!)
    }

    /**
     * Test method for
     * [org.apache.commons.graph.model.BaseGraph.getEdge]
     */
    @Test
    fun testGetNotExistsEdge() {
        // Test a complete undirect graph.
        val g =
            UndirectedMutableGraph<BaseLabeledVertex, BaseLabeledEdge>()
        // building Graph
        for (i in 0..3) {
            val v = BaseLabeledVertex(i.toString())
            g.addVertex(v)
        }

        val source = BaseLabeledVertex(1.toString())
        val target = BaseLabeledVertex(2.toString())
        val edge = g.getEdge(source, target)
        Assert.assertNull(edge)
    }

    /**
     * Test Graph model ina multi-thread enviroment.
     */
    @Test
    @Throws(Throwable::class)
    fun testMultiThreadUndirectGraph() {
        val g =
            synchronize(UndirectedMutableGraph<BaseLabeledVertex, BaseLabeledEdge>() as MutableGraph<BaseLabeledVertex, BaseLabeledEdge>) as MutableGraph<BaseLabeledVertex, BaseLabeledEdge>
        val tr1: TestRunner = GraphInsert(g, 0, 10)
        val tr2: TestRunner = GraphInsert(g, 10, 20)
        val tr3: TestRunner = GraphInsert(g, 20, 30)

        val trs = arrayOf(tr1, tr2, tr3)
        val mttr = MultiThreadedTestRunner(trs)

        mttr.runRunnables()

        assertEquals(30, g.order)

        // test the # of edges = n (n-1)/2
        assertEquals((30 * (30 - 1) / 2), g.size)
    }

    /**
     * Test method for [org.apache.commons.graph.model.BaseMutableGraph.removeEdge]
     */
    @Test
    fun testUndirectedGraphRemoveEdge() {
        // Test a complete undirect graph.
        val g = UndirectedMutableGraph<BaseLabeledVertex, BaseLabeledEdge>()

        val source = BaseLabeledVertex(1.toString())
        val target = BaseLabeledVertex(2.toString())

        GraphUtils.buildCompleteGraph(10, g)

        val e = g.getEdge(source, target)!!
        g.removeEdge(e)

        val edge = g.getEdge(source, target)

        Assert.assertNull(edge)
    }

    /**
     * Test method for [org.apache.commons.graph.model.BaseMutableGraph.removeEdge]
     */
    @Test(expected = GraphException::class)
    fun testUndirectedGraphRemoveEdgeNotExists() {
        // Test a complete undirect graph.
        var g: UndirectedMutableGraph<BaseLabeledVertex, BaseLabeledEdge>? = null
        var e: BaseLabeledEdge? = null
        try {
            g = UndirectedMutableGraph()
            GraphUtils.buildCompleteGraph(10, g)

            e = BaseLabeledEdge("NOT EXIST")
        } catch (ex: GraphException) {
            Assert.fail(ex.message)
        }

        g!!.removeEdge(e!!)
    }
}
